import os
import json
import re
import urllib
import sys
from BeautifulSoup import BeautifulSoup

#encapsulating the whole thing in a try for error logging
#opens UC's class URL and reads the HTML into BeautifulSoup
r = raw_input('Last two digits of year (2011 = 11): ')
t = raw_input('Term (A, W, S, U): ')
s = raw_input('File to save: ')
f = open(s, 'w')
print 'started...'
site = "https://webapps.uc.edu/onestop/learningopp/searchresults.asp"
fields=(
    ("termcode" , str(r+t)), 
    ("alltermcode" , "'11A', '11W', '11U', '11S'"), 
    ("collcode" , ""), 
    ("day1" , "M"), 
    ("day1" , "T"), 
    ("day1" , "W"), 
    ("day1" , "H"), 
    ("day1" , "F"), 
    ("timeselect" , "any"), 
    ("bok" , ""), 
    ("deliverymode" , ""), 
    ("keyword", "")
)
data = urllib.urlencode(fields,doseq=True)
ucURL = urllib.urlopen(site, data)
trList = BeautifulSoup(ucURL.read()).findAll('tr', bgcolor=['FFFFFF', 'CCCCCC'])
#trList = BeautifulSoup(open('search.html').read()).findAll('tr', bgcolor=['FFFFFF', 'CCCCCC'])
print 'loaded html'
#init variables
lastTR = ''
json = '{ "courses" : [ \n'
jsonObj = []
first = True
sectionFirst = True
ignore = 0
flexSchedule = re.compile("(;)(?P<dates>\w* \d{1,2} - \w* \d{1,2})")
regex = re.compile("(?P<quarter>[0-9]{2}(A|W|U|S))((\S+)(\W+))*(?P<section>[0-9]{3})")
timeRegex = re.compile("(?P<startTime>(\d{0,2}):(\d{2}))-(?P<endTime>(\d{0,2}):(\d{2}))")
classspec = re.compile("((?P<college>\d{2})( )?(?P<discipline>[A-Z]{2,4})( )?(?P<class>\d{3})(?P<isHonors>(H)?))")
creditRegex = re.compile("([(-](?P<creditNum>\d{1,2})(?P<isGraduate>([G*])?)[)])")
courseNameRegex = re.compile("popCourse[(](?P<number>(\d+)?)[)]")

classesTotal = 0
lenCourse = 0
#loop through all <tr> elements in the HTML
for idx,tr in enumerate(trList):
    if tr['bgcolor'] == lastTR and tr.find('table') == None:
        if tr.nextSibling != '\n\n':
                if ignore > 0:
                                ignore = ignore - 1
                else:
                                try:                                        
                                        sectionNum              = regex.search(str(tr.contents[1].find('font'))).group("section")                        
                                        callNum                 = tr.contents[3].find('i').string.replace('&nbsp;','')
                                        days = '{"days": '
                                        day                             = tr.contents[5].find('font').string.replace('&nbsp;','')
                                        startTime                       = timeRegex.search(str(tr.contents[7].find('font'))).group('startTime') 
                                        endTime                         = timeRegex.search(str(tr.contents[7].find('font'))).group('endTime')
                                        days += '"' + day.strip() + '","startTime": "' + startTime + '","endTime": "' + endTime + '"}'                        
                                except IndexError,e:
                                        print "Index Error: %s" % e
                                        print str(tr)
                                        #manually enter section info
                                        a = raw_input("Error with previous class (see line above). Manually enter section [y/n]?")
                                        if a == "n" or a == "N" or a == "no" or a == "No":
                                                pass
                                        else:
                                                sectionNum = raw_input("Section number: ")
                                                callNum = raw_input("Call number: ")
                                                startTime = raw_input("Start time: ")
                                                endTime = raw_input("End time: ")
                                                days = '{"days": '
                                                day = raw_input("Days (eg MWF): ")
                                                days += '"' + day.strip() + '","startTime": "' + startTime + '","endTime": "' + endTime + '"}'                        
                                except Exception,e:
                                        print e
                                        print str(tr)
                                        a = raw_input("waiting...")
                                        pass
                                        
                                trsub = tr.nextSibling
                                while trsub == '\n':
                                        trsub = trsub.nextSibling
                                flexDates = ''
                                if trsub != None:
                                                try:
                                                        if flexSchedule.search(str(trsub.contents[0].find('font'))):
                                                                flexDates = flexSchedule.search(str(trsub.contents[0].find('font'))).group('dates').strip()
                                                                ignore += 1
                                                except IndexError:
                                                        pass
                                                try:
                                                        while not (regex.search(str(trsub.contents[1].find('font')))) and trsub.contents[5].find('font').string != None:
                                                                ignore += 1
                                                                days += ',{"days": '
                                                                day                             = trsub.contents[5].find('font').string.replace('&nbsp;','')
                                                                startTime                       = timeRegex.search(str(trsub.contents[7].find('font'))).group('startTime') 
                                                                endTime                         = timeRegex.search(str(trsub.contents[7].find('font'))).group('endTime') 
                                                                days                            += '"' + day.strip() + '","startTime": "' + startTime + '","endTime": "' + endTime + '"}'
                                                                trsub = trsub.nextSibling                                        
                                                except IndexError:
                                                        pass
                                instructor = tr.contents[9].find('font').string.replace('&nbsp;','').strip() if tr.contents[9].find('font') != None and tr.contents[9].find('font').string != None else ''
                                location = tr.contents[11].find('font').string.replace('&nbsp;','') if tr.contents[11].find('font') != None and tr.contents[11].find('font').string != None else ''
                                if sectionFirst:
                                        json += ' "flexDates" : "%s",\n ' % flexDates if not (flexDates == '') else ''
                                        json += '\n"sections": [{ "sectionNum" : "%s",\n  ' % sectionNum
                                        sectionFirst = False
                                else:
                                        json += ',\n{ "sectionNum" : "%s",\n  ' % sectionNum
                                json += ' "callNum" : "%s",\n ' % callNum
                                json += ' "schedule" : [%s],\n ' % days
                                json += ' "instructor" : "%s",\n ' % instructor
                                json += ' "location" : "%s"\n ' % location
                                json += ' }'
    elif tr.find('table') == None:                
                try:     
                        course = tr.contents[5].find('a').string.replace('&nbsp;','')
                        longName = ''
                        desc = ''
                        #try to get each courses full name from UC's servers
                        try:
                            courseID = courseNameRegex.search(str(tr.contents[5].find('a'))).group('number')
                            nameSite = "https://webapps.uc.edu/onestop/learningopp/courseDetail.asp?course_ID=%s" % courseID
                            subSite = urllib.urlopen(nameSite)
                            subHTML = BeautifulSoup(subSite.read())
                            longName = subHTML.findAll('font', color=["#FF0000"])[0].string.replace("'","''").replace('"',"''")
                            desc = subHTML.findAll('font')[2].string.replace("'","''").replace('"',"''")
                        except Exception as ex:
                                pass
                                
                        courseinfo = classspec.search(str(tr.contents[1].find('font')))
                        courseNum = courseinfo.group('class')
                        college = courseinfo.group('college')
                        discipline = courseinfo.group('discipline')
                        isHonors = 1 if(courseinfo.group('isHonors')) else 0
                        bok = tr.contents[9].find('a').string.replace('&nbsp;','') if tr.contents[9].find('a') != None and tr.contents[9].find('a').string != None else ''
                        creditInfo = creditRegex.search(str(tr.contents[3].find('b')))
                        credits = creditInfo.group('creditNum')
                        isGraduate = creditInfo.group('isGraduate') if(creditInfo.group('isGraduate')) else 0
                        if isGraduate == '*':
                                isGraduate = 2
                        elif isGraduate == 'G':
                                isGraduate = 1
                        prereqs = ",".join(['{ "college": "' + item['college'] +
                                                                '","discipline": "' + item['discipline'] +
                                                                '","courseNumber": "' + item['class'] + '"}'
                                                                for item in [m.groupdict() for m in classspec.finditer(str(tr.contents[7].find('i')))]
                                                                ])
                except IndexError,e:
                        print "Index Error: %s" % e
                        print str(tr)
                        a = raw_input("Error with next course. Manually enter class [y/n]?")
                        if(a == "n" or a == "N" or a == "no" or a == "No"):
                                pass
                        else:
                                #manually enter class information
                                course = raw_input("Abbreviated name: ")
                                longName = raw_input("Long name: ")
                                desc = raw_input("Description: ")
                                courseNum = raw_input("Course number (eg 101): ")
                                college = raw_input("College: ")
                                discipline = raw_input("Discipline: ")
                                isHonors = raw_input("Honors (1 for yes, 0 for no): ")
                                isHonors = isHonors if isHonors == 0 or isHonors == 1 else 0
                                isGraduate = raw_input("Grad course (0 for no, 1 for grad only, 2 for joint): ")
                                isGraduate = isGraduate if isGraduate == 0 or isGraduate == 1 or isGraduate == 2 else 0
                                bok = raw_input("BoK: ")
                                credits = raw_input("Credit hours: ")
                                prereqs = raw_input("Prereq: ")
                
                sectionFirst    = True
                classesTotal += 1
                if len(longName) > lenCourse:
                                lenCourse = len(longName)
                print course
                if first:
                        json += '\n{"course" : "%s",\n ' % course.strip()
                        first = False
                else:
                        json += ']}, \n{"course" : "%s",\n ' % course.strip()
                json += '"longName" : "%s", \n ' % longName
                json += '"desc" : "%s", \n ' % desc
                json += '"courseNum" : "%s",\n ' % courseNum
                json += '"college" : "%s",\n ' % college
                json += '"discipline" : "%s",\n ' % discipline
                json += '"isHonors": "%s",\n ' % isHonors
                json += '"isGraduate": "%s",\n ' % isGraduate
                json += '"bok" : "%s",\n ' % bok
                json += '"credits" : "%s",\n ' % credits
                json += '"prereqs" : [%s],\n ' % prereqs
                lastTR = tr['bgcolor']          

json += '\n]}\n]}'

#for dealing w/ nasty characters
f.write(json.encode('ascii','ignore'))
f.close()

print '%d classes found\n' % classesTotal
print '%d is maximum course name length' % lenCourse
